#ifndef CKKS_SRC_NUMB_H_
#define CKKS_SRC_NUMB_H_

#include <set>

#include <arm_neon.h>
#include <ctime>

#include "common.h"

using uint128_t = unsigned __int128;

void Negate(uint64_t &r, uint64_t a);

void AddMod(uint64_t &r, uint64_t a, uint64_t b, uint64_t p);

uint64_t SingleBarrettNew(__uint128_t &a, uint64_t q, uint64_t muHigh, uint64_t muLow);

uint64_t Barrett128(__uint128_t &a, uint64_t q, uint64_t mu_high, uint64_t mu_low);

uint64_t Barrett64(uint64_t a, uint64_t q, uint64_t mu);

void Barrett64NEON(uint64x2_t *res, uint64x2_t a, uint64x2_t q, uint64x2_t mu);

void AddModAndEqual(uint64_t &a, uint64_t b, uint64_t p);

void SubMod(uint64_t &r, uint64_t a, uint64_t b, uint64_t p);

void SubModAndEqual(uint64_t &a, uint64_t b, uint64_t p);

void MulMod(uint64_t &r, uint64_t a, uint64_t b, uint64_t p);

void MulModBarrett(uint64_t &r, uint64_t a, uint64_t b, uint64_t p, uint64_t twop, uint64_t prHigh, uint64_t prLow);

void ModBarrett(uint64_t &r, uint64_t a, uint64_t p, uint64_t prHigh, uint64_t prLow);

void ModBarrett(uint64_t &r, uint128_t a, uint64_t p, uint64_t prHigh, uint64_t prLow);

uint64_t InvMod(uint64_t x, uint64_t p);

uint64_t PowMod(uint64_t x, uint64_t y, uint64_t p);

uint64_t Inv(uint64_t x);

uint64_t Pow(uint64_t x, uint64_t y);

uint32_t BitReverse(uint32_t x);

uint64_t Gcd(uint64_t a, uint64_t b);

long Gcd(long a, long b);

void FindPrimeFactors(std::set<uint64_t> &s, uint64_t number);

uint64_t FindPrimitiveRoot(uint64_t p);

uint64_t FindMthRootOfUnity(uint64_t M, uint64_t p);

uint64_t xShoup(uint64_t x, uint64_t p);

uint64_t ShoupMul(uint64_t a, uint64_t b, uint64_t b_shoup, uint64_t q);

void MulModShoup(uint64_t &res, uint64_t a, uint64_t b, uint64_t bShoup, uint64_t q);

void MulModBarrettNeon(uint64x2_t *r, uint64x2_t a, uint64x2_t b, uint64x2_t p, uint64x2_t twop, uint64x2_t prHigh,
                       uint64x2_t prLow);

void AddModNEON(uint64x2_t *r, uint64x2_t a, uint64x2_t b, uint64x2_t p);

void ShoupMulNEON(uint64x2_t* result, uint64x2_t a, uint64x2_t b, uint64x2_t bShoup, uint64x2_t q);

void ComSubQ(uint64_t& a, uint64_t q);

void ComSubQNeon(uint64x2_t &a, uint64x2_t q);

#endif
